<?php

namespace App\Libs;

class ZmqReq
{
    const SIGN_KEY = 'pub';

    /**
     * @param string $addr: ZeroMQ socket server addr.
     * @param string $action: server side action.
     * @param array $payload: request data.
     * @param int $sockType: ZeroMQ socket type. default to \ZMQ::SOCKET_REQ.
     * @return array|bool
     */
    public function request($addr, $action, array $payload, $sockType = \ZMQ::SOCKET_REQ , $corp = 1)
    {
        $sock = $this->getSocket($addr, $sockType);

        $boxed = [
            'data' => $payload,
            'action' => $action,
            'version' => '0.0.1',
            'nonce' => base64_encode(random_bytes(16)),
            'time' => time(),
        ];

        $boxedJson = json_encode($boxed);
        $secret = \Config::get('app.sign.' . self::SIGN_KEY);
        $sign = md5(self::SIGN_KEY . '|' . $boxedJson . '|' . $secret);
        $enveloped = [
            'data' => $boxedJson,
            'key' => self::SIGN_KEY,
            'sign' => $sign,
        ];
        $envelopedJson = json_encode($enveloped);

        if($sockType == \ZMQ::SOCKET_PUB){
            $sock->send("{$corp} {$envelopedJson}");
        }else{
            $sock->send($envelopedJson);
        }

        if ($sockType !== \ZMQ::SOCKET_REQ) {
            return true;
        }

        $resJson = $sock->recv();
        $resEnveloped = json_decode($resJson, true);
        if (!empty($resEnveloped['data'])) {
            $resBoxed = json_decode($resEnveloped['data'], true);
            if (isset($resBoxed['data']['code']) and $resBoxed['data']['code'] === 0) {
                return $resBoxed['data']['data'];
            }
        }

        \Log::warning('failed on ZeroMQ request, addr: ' . $addr
            . ', payload: ' . var_export($payload, true) . ', res: ' . $resJson);
        return false;
    }

    /**
     * @param string $addr: ZeroMQ socket server addr.
     * @param int $type: ZeroMQ socket type.
     * @return \ZMQSocket
     * @throws \Exception
     */
    protected function getSocket($addr, $type)
    {
        if (!$addr) {
            throw new \Exception('bad addr: ' . var_export($addr, true));
        }
        $persistId = base64_encode($addr) . ':' . base64_encode(strval($type));
        return new \ZMQSocket(new \ZMQContext(), $type, $persistId, function(\ZMQSocket $s) use($addr, $type) {
            \Log::info("ZeroMQ socket connecting to {$addr}");
            $s->connect($addr);
            if ($type === \ZMQ::SOCKET_PUB) {
                sleep(\Config::get('app.notice_sleep') ?: 5);
            }
        });
    }
}